use crate::db::{dao, po};
use crate::dto;
use crate::service::error::Error;
use std::sync::Arc;
use wanmesh_common::dto::ResponseCode;
use wanmesh_common::error::ResponseError;

#[async_trait::async_trait]
pub trait EndpointPeerService: Send + Sync {
    async fn add(
        &self,
        params: &dto::core::endpoint_peer::AddParams,
    ) -> Result<dto::core::endpoint_peer::AddResponseData, Error>;

    async fn list(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::endpoint_peer::EndpointPeerItem>, Error>;

    async fn all(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
    ) -> Result<Vec<dto::core::endpoint_peer::EndpointPeerItem>, Error>;

    async fn get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint_peer::EndpointPeerItem, Error>;

    async fn try_get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint_peer::EndpointPeerItem>, Error>;
    async fn delete(&self, endpoint_peer_id: &str) -> Result<(), Error>;

    async fn count(&self, filter: &dto::core::endpoint_peer::Filter) -> Result<u64, Error>;
    async fn exists(&self, filter: &dto::core::endpoint_peer::Filter) -> Result<bool, Error>;
}

pub struct DefaultEndpointPeerService {
    endpoint_peer_dao: Arc<dyn dao::endpoint_peer::EndpointPeerDao>,
}

impl DefaultEndpointPeerService {
    pub fn new(endpoint_peer_dao: Arc<dyn dao::endpoint_peer::EndpointPeerDao>) -> Self {
        Self { endpoint_peer_dao }
    }
}

#[async_trait::async_trait]
impl EndpointPeerService for DefaultEndpointPeerService {
    async fn add(
        &self,
        params: &dto::core::endpoint_peer::AddParams,
    ) -> Result<dto::core::endpoint_peer::AddResponseData, Error> {
        if params.endpoint_id == params.peer_endpoint_id {
            return Err(Error::ResponseError(ResponseError {
                code: ResponseCode::BAD_REQUEST,
                message: "endpoint_id和peer_endpoint_id不能相同".to_string(),
                backtrace: Default::default(),
            }));
        }

        let endpoint_peer_id = wanmesh_common::util::new_id()?;

        self.endpoint_peer_dao
            .insert(po::endpoint_peer::ActiveModel {
                id: Default::default(),
                endpoint_peer_id: po::ActiveValue::Set(endpoint_peer_id.clone()),
                endpoint_id: po::ActiveValue::Set(params.endpoint_id.clone()),
                peer_endpoint_id: po::ActiveValue::Set(params.peer_endpoint_id.clone()),
                create_timestamp_millis: Default::default(),
                update_timestamp_millis: Default::default(),
            })
            .await?;

        Ok(dto::core::endpoint_peer::AddResponseData { endpoint_peer_id })
    }

    async fn list(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::endpoint_peer::EndpointPeerItem>, Error> {
        let list = self
            .endpoint_peer_dao
            .find(dao::FindParams {
                filter: filter.into(),
                offset: Some(offset),
                limit: Some(limit),
                ..Default::default()
            })
            .await?
            .iter()
            .map(|m| m.into())
            .collect();

        Ok(list)
    }

    async fn all(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
    ) -> Result<Vec<dto::core::endpoint_peer::EndpointPeerItem>, Error> {
        if filter == &Default::default() {
            return Err(Error::ResponseError(
                ResponseError::filter_can_not_be_empty(),
            ));
        }

        let list = self
            .endpoint_peer_dao
            .find(dao::FindParams {
                filter: filter.into(),
                ..Default::default()
            })
            .await?
            .iter()
            .map(|m| m.into())
            .collect();

        Ok(list)
    }

    async fn get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint_peer::EndpointPeerItem, Error> {
        let endpoint_peer = self
            .try_get(endpoint_peer_id)
            .await?
            .ok_or(ResponseError::endpoint_peer_not_found(endpoint_peer_id))?;
        Ok(endpoint_peer)
    }

    async fn try_get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint_peer::EndpointPeerItem>, Error> {
        let filter = dao::endpoint_peer::Filter {
            endpoint_peer_id: Some(endpoint_peer_id.to_string()),
            ..Default::default()
        };

        if let Some(m) = self.endpoint_peer_dao.try_get(filter).await? {
            return Ok(Some(m.into()));
        }

        Ok(None)
    }

    async fn delete(&self, endpoint_peer_id: &str) -> Result<(), Error> {
        let filter = dao::endpoint_peer::Filter {
            endpoint_peer_id: Some(endpoint_peer_id.to_string()),
            ..Default::default()
        };
        self.endpoint_peer_dao.delete(filter).await?;
        Ok(())
    }

    async fn count(&self, filter: &dto::core::endpoint_peer::Filter) -> Result<u64, Error> {
        let count = self.endpoint_peer_dao.count(filter.into()).await?;
        Ok(count)
    }

    async fn exists(&self, filter: &dto::core::endpoint_peer::Filter) -> Result<bool, Error> {
        let filter = dao::endpoint_peer::Filter::from(filter);
        let exists = self.endpoint_peer_dao.exists(filter).await?;
        Ok(exists)
    }
}

impl From<&po::endpoint_peer::Model> for dto::core::endpoint_peer::EndpointPeerItem {
    fn from(value: &po::endpoint_peer::Model) -> Self {
        Self {
            endpoint_peer_id: value.endpoint_peer_id.clone(),
            inner: dto::core::endpoint_peer::EndpointPeer {
                endpoint_id: value.endpoint_id.clone(),
                peer_endpoint_id: value.peer_endpoint_id.clone(),
            },
        }
    }
}

impl From<po::endpoint_peer::Model> for dto::core::endpoint_peer::EndpointPeerItem {
    fn from(value: po::endpoint_peer::Model) -> Self {
        Self::from(&value)
    }
}

impl From<&dto::core::endpoint_peer::Filter> for dao::endpoint_peer::Filter {
    fn from(value: &dto::core::endpoint_peer::Filter) -> Self {
        Self {
            endpoint_peer_id: value.endpoint_peer_id.clone(),
            endpoint_id: value.endpoint_id.clone(),
            peer_endpoint_id: value.peer_endpoint_id.clone(),
        }
    }
}
